﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: ObjectSearcher_1.cs
//
//  Description: Searches for persistent objects in the storage.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Reflection;
using Chili.Opf3.Query;

namespace Chili.Opf3
{
	/// <summary>
	/// Allows to search in the storage for persistent objects of a certain type.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This class is specialized to load objects from the storage. The implementation is 
	/// gerneric: once created it allows you to load only objects of the generic type.
	/// Usually the generic ObjectSearcher class is created by using the 
	/// <see cref="Opf3.ObjectContext.GetObjectSearcher{T}()">GetObjectSearcher</see> function of the 
	/// <see cref="Opf3.ObjectContext">ObjectContext</see>.
	/// ObjectSearcher contains a few methods (for example: <see cref="Opf3.ObjectSearcher{T}.FindAll()">FindAll</see>) to
	/// search for objects.
	/// </para>
	/// <para>
	/// <see cref="Opf3.ObjectSearcher{T}.FindAll()">FindAll</see> returns an instance of the
	/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see> class. This class contains the result of the search. FindAll has been 
	/// overloaded to return also a class that implements the <see cref="System.Collections.Generic.IList{T}">IList</see> 
	/// interface with the resultset.
	/// </para>
	/// <para>
	/// The ObjectSearcher should be also used to encapsulate complex queries. If you have a complex SQL statement you
	/// should derive from ObjectSearcher and create your own "Searcher" class. To create your own "Searcher" classes derive 
	/// directly from ObjectSearcher and extend it with your own methods (For further information see the example below). 
	/// </para>
	/// </remarks>
	/// <example>
	/// The following example shows how to get an ObjectSearcher that searches for User objects. 
	/// The second example shows how to derive from ObjectSearcher to create an own searcher class.
	///		<code lang="CS">
	/// [Persistent("USER")]
	/// public class User
	/// {
	///		private string _name = null;
	/// 
	///		[Field("NAME")]
	///		public string Name
	///		{
	///			get { return _name; }
	///			set { _name = value; }
	///		}
	/// 
	///		// ... Other properties and fields.
	/// }
	/// 
	/// // ... Other code
	/// 
	/// // Get the ObjectSearcher for User.
	/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
	/// // Find all user in the storage.
	/// ObjectSet&lt;User&gt; objectSet = searcher.FindAll();
	/// 
	/// // Find only those with a given name by using conditions and sorts them.
	/// objectSet = searcher.FindAll("Name Like {0} SortBy Name Asc", "%mith%");
	///		</code>
	///		<code lang="CS">
	/// // UserSearcher is a specialized class that searches only for user objects.
	/// public class UserSearcher : ObjectSearcher&lt;User&gt;
	/// {
	///		public UserSearcher(ObjectContext context) : base(context)
	///		{
	///		}
	/// 
	///		// Returns all user with at least one call.
	///		public ObjectSet&lt;User&gt; GetWithCalls()
	///		{
	///			// In the example a SqlQuery is used, but you could also use an ObjectQuery.
	///			SqlQuery query = new SqlQuery("select distinct u.* from [USER] u, CALLS c where " + 
	///				"u.ID = c.USER_ID;", null);
	///			return Context.GetObjectSet&lt;User&gt;(query);
	///		}
	/// }
	/// 
	/// // ... Other code
	///
	/// // Creates a new ObjectContext that uses an MsSql Server as storage.
	/// ObjectContext context = new ObjectContext(new MsSqlStorage("sa", "", "localhost", "application"));
	/// // Set the MD5 Concurrency Manager.
	/// context.ConcurrencyManager = new Md5ConcurrencyManager();
	/// 
	/// // We can't use directly the methods of the <see cref="ObjectContext">ObjectContext</see>
	/// // to get the UserSearcher. We have to create an instance and pass the current context.
	/// UserSearcher searcher = new UserSearcher(context);
	/// ObjectSet&lt;User&gt; os = searcher.GetWithCalls();
	/// </code>
	/// </example>
	public class ObjectSearcher<T>
	{
		private ObjectContext _context;

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSearcher{T}">ObjectSearcher Class</see>.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> used by the 
		/// <see cref="Opf3.ObjectSearcher{T}">ObjectSearcher</see> to execute the search.</param>
		/// <exception cref="System.ArgumentNullException">Context is null.</exception>
		public ObjectSearcher(ObjectContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			_context = context;
		}

		/// <summary>
		/// Specifies the <see cref="Opf3.ObjectContext">ObjectContext</see> used by this 
		/// <see cref="Opf3.ObjectSearcher{T}">ObjectSearcher</see>.
		/// </summary>
		/// <exception cref="System.ArgumentNullException">Context is null.</exception>
		public ObjectContext Context
		{
			get { return _context; }
		}

		/// <summary>
		/// Returns an <see cref="Opf3.ObjectSet{T}">ObjectSet</see> containing all persistent objects of the generic type 
		/// that are found in the storage.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		///		<code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find all user in the storage.
		/// ObjectSet&lt;User&gt; objectSet = searcher.FindAll();
		///		</code>
		/// </example>
		public virtual ObjectSet<T> FindAll()
		{
			return this.Context.GetObjectSet<T>();
		}

		/// <summary>
		/// Returns an <see cref="Opf3.ObjectSet{T}">ObjectSet</see> of all persistent objects matching
		/// the conditions that are found in the storage.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		/// <code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find only those with a given name by using conditions.
		/// objectSet = searcher.FindAll("Name like {0} SortBy Name Asc", "%mith%");
		/// </code>
		/// </example>
		/// <param name="conditions">A condition string that specifies the objects that are loaded.</param>
		/// <param name="parameters">A list of parameters that is passed as parameters for the conditions.</param>
		public virtual ObjectSet<T> FindAll(string conditions, params object[] parameters)
		{
			return this.Context.GetObjectSet<T>(conditions, parameters);
		}

		/// <summary>
		/// Returns an <see cref="System.Collections.Generic.IList{T}">IList</see> containing all persistent objects of the 
		/// generic type that are found in the storage.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		/// <code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find all user in the storage.
		/// IList&lt;User&gt; = searcher.FindAll(new List&lt;User&gt;);
		/// </code>
		/// </example>
		/// <param name="list">A class that implements the 
		/// <see cref="System.Collections.Generic.IList{T}">IList</see> interface is populated with the 
		/// result of the search.</param>
		/// <returns>Returns the <see cref="System.Collections.Generic.IList{T}">IList</see> with the 
		/// objects returned by the search.</returns>
		public virtual IList<T> FindAll(IList<T> list)
		{
			return this.Context.GetObjectList<T>(list);
		}

		/// <summary>
		/// Returns an <see cref="System.Collections.Generic.IList{T}">IList</see> of all persistent objects matching
		/// the conditions that are found in the storage.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		/// <code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find only those with a given name by using conditions.
		/// IList&lt;User&gt; = searcher.FindAll(new List&lt;User&gt;, "Name like {0} SortBy Name Asc", "%mith%");
		/// </code>
		/// </example>
		/// <param name="list">A class that implements the 
		/// <see cref="System.Collections.Generic.IList{T}">IList</see> interface is populated with the 
		/// result of the search.</param>
		/// <param name="conditions">A condition string that specifies the objects that are loaded.</param>
		/// <param name="parameters">A list of parameters that is passed as parameters for the conditions.</param>
		/// <returns>Returns the <see cref="System.Collections.Generic.IList{T}">IList</see> with the 
		/// objects returned by the search.</returns>
		public virtual IList<T> FindAll(IList<T> list, string conditions, params object[] parameters)
		{
			return this.Context.GetObjectList<T>(list, conditions, parameters);
		}

		/// <summary>
		/// Returns the first persistent object of the generic type that are found in the storage. Or default(T) if no 
		/// item has been found.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		///		<code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find the first user in the storage.
		/// User user = searcher.FindOne();
		///		</code>
		/// </example>
		public virtual T FindOne()
		{
			return this.Context.GetObject<T>(new ObjectQuery<T>(null, null));
		}

		/// <summary>
		/// Returns the first persistent object of all items matching the conditions that are found in the storage. Or
		/// default(T) if no item has been found.
		/// </summary>
		/// <example>
		/// The example shows how this method is used in your application code.
		/// <code lang="CS">
		/// [Persistent("USER")]
		/// public class User
		/// {
		///		private string _name = null;
		/// 
		///		[Field("NAME")]
		///		public string Name
		///		{
		///			get { return _name; }
		///			set { _name = value; }
		///		}
		/// 
		///		// ... Other properties and fields.
		/// }
		/// 
		/// // ... Other code
		/// 
		/// // Get the ObjectSearcher for User.
		/// ObjectSearcher&lt;User&gt; searcher = context.GetObjectSearcher&lt;User&gt;();
		/// // Find the first with a given name by using conditions.
		/// User user = searcher.FindOne("Name like {0} SortBy Name Asc", "%mith%");
		/// </code>
		/// </example>
		/// <param name="conditions">A condition string that specifies the objects that are loaded.</param>
		/// <param name="parameters">A list of parameters that is passed as parameters for the conditions.</param>
		public virtual T FindOne(string conditions, params object[] parameters)
		{
			return Context.GetObject<T>(new ObjectQuery<T>(conditions, parameters));
		}
	}
}
